﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
namespace LogicCircuit
{
    /// Main composition class
    [Serializable]
    public class LogicCircuit
    {
        /// <summary>
        /// Main composition class. Contains lists of connections and elements. Performs actions like for adding/removeing connections, adding/removing elements, drawing
        /// </summary>
        /// 
        private List<Element> elements = new List<Element>();
        private List<Connection> connections = new List<Connection>();
        public void draw(object sender, PaintEventArgs e)
        {
            foreach (Connection c in connections)
            {
                string type = c.target.type;
                //draw and colorize.
                Pen myPen;
                int x1, y1, x2, y2;
                if (c.Value == 0)
                    myPen = new Pen(Color.Red);
                else if (c.Value == 1)
                    myPen = new Pen(Color.Green);
                else
                    myPen = new Pen(Color.Gray);

                myPen.Width = 2;
                Brush myBrush = new SolidBrush(Color.BlueViolet);
                x1 = c.supplier.X + 80;
                y1 = c.supplier.Y + 29;
                if (type == "And")
                {
                    if (((And)c.target).InputA == c)
                    {
                        x2 = c.target.X;
                        y2 = c.target.Y + 14;
                    }
                    else
                    {
                        x2 = c.target.X;
                        y2 = c.target.Y + 44;
                    }
                }
                else if (type == "Or")
                {
                    if (((Or)c.target).InputA == c)
                    {
                        x2 = c.target.X;
                        y2 = c.target.Y + 14;
                    }
                    else
                    {
                        x2 = c.target.X;
                        y2 = c.target.Y + 44;
                    }
                }
                else
                {
                    x2 = c.target.X;
                    y2 = c.target.Y + 29;
                }
                e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                e.Graphics.DrawLine(myPen, x1, y1, x2, y2);

            }
            foreach (Element elem in elements)
            {
                string type = elem.type;
                if (type == "Sink")
                {
                    if (((Sink)elem).Input != null)
                        if (((Sink)elem).Input.Value == 1)
                            e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.Sink1, elem.X, elem.Y, 80, 60);
                        else
                            e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.Sink0, elem.X, elem.Y, 80, 60);
                    else
                        e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.Sink0, elem.X, elem.Y, 80, 60);
                }
                else
                {
                    switch (type)
                    {
                        case "Source0": e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.Source0, elem.X, elem.Y, 80, 60); break;
                        case "Source1": e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.Source1, elem.X, elem.Y, 80, 60); break;
                        case "And": e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.And, elem.X, elem.Y, 80, 60); break;
                        case "Or": e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.Or, elem.X, elem.Y, 80, 60); break;
                        case "Not": e.Graphics.DrawImage(global::LogicCircuit.Properties.Resources.Not, elem.X, elem.Y, 80, 60); break;
                    }
                }  
            }
        }
        public void draw(PictureBox pb)
        {
            pb.Invalidate();
        }

        /// <param name="supX">X cooridnate of the supplier</param>pubi
        /// 
        public bool addConnex(int supX, int supY, int tarX, int tarY)
        {
            //Element size: 80x60
            //Clickbox size: 20x10
            //Element X=0, Y=0;
            //Output clickboxbox: X=60, Y=25 
            //Input clickbox: X=0, Y=25
            //InputA clickbox: X=0, Y=10 
            //InputB clickbox: X=0, Y=40 

            //get types of elements involved
            Element supplier = getElement(supX, supY);
            Element target = getElement(tarX, tarY);

            if (target.type != "Source0" && target.type != "Source1" && target != supplier)
            {
                if (target.X < supplier.X + 80)
                    return false;
                foreach (Connection con in connections)
                {
                    if (con.supplier == supplier || (con.supplier == target && con.target == supplier))
                        return false;
                }
                Connection c = new Connection(supplier, target);
                if (target.type == "And")
                {
                    if ((tarX >= target.X && tarX <= target.X + 19) && (tarY >= target.Y + 10 && tarY <= target.Y + 19))
                    {
                        if (((And)target).InputA == null)
                            ((And)target).InputA = c;
                        else
                            return false;
                    }
                    else if ((tarX >= target.X && tarX <= target.X + 19) && (tarY >= target.Y + 40 && tarY <= target.Y + 49))
                        if (((And)target).InputB == null)
                            ((And)target).InputB = c;
                        else
                            return false;
                    else
                        return false;
                }
                else if (target.type == "Or")
                {
                    if ((tarX >= target.X && tarX <= target.X + 19) && (tarY >= target.Y + 10 && tarY <= target.Y + 19))
                        if (((Or)target).InputA == null)
                            ((Or)target).InputA = c;
                        else
                            return false;
                    else if ((tarX >= target.X && tarX <= target.X + 19) && (tarY >= target.Y + 40 && tarY <= target.Y + 49))
                        if (((Or)target).InputB == null)
                            ((Or)target).InputB = c;
                        else
                            return false;
                    else
                        return false;
                }
                else if (target.type == "Not")
                    if (((Not)target).Input == null)
                        ((Not)target).Input = c;
                    else
                        return false;
                else if (target.type == "Sink")
                    if (((Sink)target).Input == null)
                        ((Sink)target).Input = c;
                    else
                        return false;
                connections.Add(c);
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool addElement(int x, int y, string type)
        {
            int panelWidth = 991;
            int panelHeight = 583;
            int direction = 0;
            int magnifier = 1;
            int baseX = x;
            int baseY = y;

            if (x > 0 && y > 0 && x <= panelWidth - 80 && y <= panelHeight - 60)
            {
                while (isElement(x, y) || isElement(x + 79, y) ||
                        isElement(x + 79, y + 59) || isElement(x, y + 59))
                {
                    if (x > 0 && y > 0 && x <= panelWidth - 80 && y <= panelHeight - 60)
                    {
                        if (direction == 0) //east
                        {
                            x = baseX + 1 * magnifier;
                            y = baseY;
                        }
                        else if (direction == 1) //south-east
                        {
                            x = baseX + 1 * magnifier;
                            y = baseY + 1 * magnifier;
                        }
                        else if (direction == 2) // south
                        {
                            x = baseX;
                            y += 1 * magnifier;
                        }
                        else if (direction == 3) //south-west
                        {
                            x = baseX - 1 * magnifier;
                            y = baseY + 1 * magnifier;
                        }
                        else if (direction == 4) //west
                        {
                            x = baseX - 1 * magnifier;
                            y = baseY;
                        }

                        else if (direction == 5) //north-west
                        {
                            x = baseX - 1 * magnifier;
                            y = baseY - 1 * magnifier;
                        }

                        else if (direction == 6) //north
                        {
                            x = baseX;
                            y = baseY - 1 * magnifier;
                        }
                        else if (direction == 7) //north-west
                        {
                            x = baseX + 1 * magnifier;
                            y = baseY - 1 * magnifier;
                            magnifier++;
                            direction = -1;
                        }
                    }
                    else
                    {
                        return false;
                    }
                    direction++;
                }

                Element element;
                switch (type)
                {
                    case "Source": element = new Source(x, y, 0); elements.Add(element); break;
                    case "Source0": element = new Source(x, y, 0); elements.Add(element); break;
                    case "Source1": element = new Source(x, y, 1); elements.Add(element); break;
                    case "And": element = new And(x, y); elements.Add(element); break;
                    case "Or": element = new Or(x, y); elements.Add(element); break;
                    case "Not": element = new Not(x, y); elements.Add(element); break;
                    case "Sink": element = new Sink(x, y); elements.Add(element); break;
                }
                return true;
            }
            else
                return false;
        }

        public void clearAll()
        {
            elements.Clear();
            connections.Clear();
        }

        public void toggleSource(int x, int y)
        {
            Element el = getElement(x,y);
            string type = el.type;

            if (el.type == "Source0")
            {
                el.type = "Source1";
                ((Source)el).Output = 1;
            }
            else if (el.type == "Source1")
            {
                el.type = "Source0";
                ((Source)el).Output = 0;
            }
                
        }

        public void removeElement(int x, int y)
        {
            Element el = getElement(x, y);
            if (el != null)
            {
                List<Connection> temp_list = new List<Connection>(connections);
                foreach (Connection c in temp_list)
                {
                    if (c.supplier == el)
                    {
                        if (c.target.type == "And")
                        {
                            if (c == ((And)c.target).InputA)
                                ((And)c.target).InputA = null;
                            else
                                ((And)c.target).InputB = null;
                        }

                        else if (c.target.type == "Or")
                        {
                            if (c == ((Or)c.target).InputA)
                                ((Or)c.target).InputA = null;
                            else
                                ((Or)c.target).InputB = null;
                        }

                        else if (c.target.type == "Not")
                        {
                            ((Not)c.target).Input = null;
                        }
                        else if (c.target.type == "Sink")
                        {
                            ((Sink)c.target).Input = null;
                        }
                        connections.Remove(c);
                    }
                    else if (c.target == el)
                    {
                        if (c.target.type == "Source0" || c.target.type == "Source1")
                            ((Source)c.target).Output = -1;
                        else if (c.target.type == "And")
                            ((And)c.target).Output = -1;
                        else if (c.target.type == "Or")
                            ((Or)c.target).Output = -1;
                        else if (c.target.type == "Not")
                            ((Not)c.target).Output = -1;
                        connections.Remove(c);
                    }
                }
                elements.Remove(el);
            }
        }

        /// <summary>
        /// returns a element if it exists on the list otherwise returns null!
        /// </summary>
        /// <param name="x">position of the element (x axis)</param>
        /// <param name="y">position of the element (y axis)</param>
        private Element getElement(int x, int y)
        {
            foreach (Element elm in elements)
            {
                if (elm.X <= x && x <= (elm.X + 79))
                {
                    if (elm.Y <= y && y <= (elm.Y + 59))
                    {
                        return elm;
                    }
                }
            }
            return null;
        }

        public void saveFile(string path)
        {
            FileStream fs = null;
            BinaryFormatter bf = null;
            List<object> elementsAndConnections = new List<object>();
            elementsAndConnections.Add(elements);
            elementsAndConnections.Add(connections);
            try
            {
                fs = new FileStream(path, FileMode.Create, FileAccess.Write);
                bf = new BinaryFormatter();
                bf.Serialize(fs, elementsAndConnections);
            }
            catch (SerializationException)
            {
                MessageBox.Show("File not savedSerialization");
            }
            catch (IOException)
            {
                MessageBox.Show("File Not Saved! \nMake sure you choose a correct location and filename or your drive is not full!");
            }
            finally
            {
                if (fs != null) fs.Close();
            }
            // Ready
        }

        public bool loadFile(string path)
        {
            FileStream fs = null;
            BinaryFormatter bf = null;
            List<object> elementsAndConnections = new List<object>();

            try
            {
                fs = new FileStream(path, FileMode.Open, FileAccess.Read);
                bf = new BinaryFormatter();
                elementsAndConnections = ((List<object>)(bf.Deserialize(fs)));
                elements = (List<Element>)elementsAndConnections[0];
                connections = (List<Connection>)elementsAndConnections[1];
            }
            catch (SerializationException)
            {
                //Serialization Error
                return false;
            }
            catch (IOException)
            {
                //IO Error
                return false;
            }
            finally
            {
                if (fs != null) fs.Close();
            }
            return true;
        }

        public bool isElement(int x, int y)
        {
            if (getElement(x, y) != null)
                return true;
            else
                return false;
        }

        public int isInputOutput(int x, int y)
        {
            Element el = getElement(x, y);

            if (el != null)
            {
                string type = el.type;
                if (type == "Source0" || type == "Source1")
                {
                    if (el.X + 60 <= x && y >= el.Y + 25 &&
                        el.X + 79 >= x && y <= el.Y + 34)
                        return 0;
                    else
                        return -1;
                }
                else if (type == "Sink")
                {
                    if (el.X <= x && y >= el.Y + 25 &&
                        el.X + 19 >= x && y <= el.Y + 34)
                    {
                        return 1;
                    }
                    else
                        return -1;
                }
                else if (type == "Not")
                {
                    if ((el.X <= x && y >= el.Y + 25 &&
                        el.X + 19 >= x && y <= el.Y + 34))
                        return 1;
                    else if ((el.X + 60 <= x && y >= el.Y + 25 &&
                            el.X + 79 >= x && y <= el.Y + 34))
                    {
                        return 0;
                    }
                    else
                        return -1;
                }
                else
                {
                    if ((el.X <= x && y >= el.Y + 10 &&
                        el.X + 19 >= x && y <= el.Y + 19) ||
                        (el.X <= x && y >= el.Y + 40 &&
                        el.X + 19 >= x && y <= el.Y + 49))
                        return 1;
                    else if (el.X + 60 <= x && y >= el.Y + 25 &&
                        el.X + 79 >= x && y <= el.Y + 34)
                    {
                        return 0;
                    }
                    else
                        return -1;
                }
            }
            else
                return -1;
        }

        public void removeConnex(int x, int y)
        {
            Element el = getElement(x, y);
            List<Connection> temp_list = new List<Connection>(connections);
            if (el.X <= x && x <= el.X + 19 &&
                el.Y + 10 <= y && y <= el.Y + 19)
            {
                foreach (Connection c in temp_list)
                {
                    if (c.target == el)
                    {
                        if (el.type == "Or")
                        {
                            if (((Or)el).InputA == c)
                            {
                                connections.Remove(c);
                                ((Or)el).InputA = null;
                                return;
                            }
                        }
                        else if (el.type == "And")
                        {
                            if (((And)el).InputA == c)
                            {
                                connections.Remove(c);
                                ((And)el).InputA = null;
                                return;
                            }
                        }

                    }
                }
            }

            else if (el.X <= x && x <= el.X + 19 &&
                     el.Y + 25 <= y + 10 && y <= el.Y + 34)
            {
                foreach (Connection c in temp_list)
                {
                    if (c.target == el)
                    {
                        if (el.type == "Not")
                        {
                            if (((Not)el).Input == c)
                            {
                                connections.Remove(c);
                                ((Not)el).Input = null;
                                return;
                            }

                            else if (((Sink)el).Input == c)
                            {
                                connections.Remove(c);
                                ((Sink)el).Input = null;
                                return;
                            }
                        }
                    }
                }
            }

            else if (el.X <= x && x <= el.X + 19 &&
                    el.Y + 40 <= y && y <= el.Y + 59)
            {
                foreach (Connection c in temp_list)
                {
                    if (c.target == el)
                    {
                        if (el.type == "Or")
                        {
                            if (((Or)el).InputB == c)
                            {
                                connections.Remove(c);
                                ((Or)el).InputB = null;
                                return;
                            }
                        }
                        else if (el.type == "And")
                        {
                            if (((And)el).InputB == c)
                            {
                                connections.Remove(c);
                                ((And)el).InputB = null;
                                return;
                            }
                        }
                    }
                }
            }
            else if (el.X + 60 <= x && x <= el.X + 79 &&
                    el.Y + 25 <= y && y <= el.Y + 34)
            {
                foreach (Connection c in temp_list)
                {
                    if (c.supplier == el)
                    {
                        Element tar = c.target;
                        if (tar.type == "And")
                        {
                            if (((And)tar).InputA == c)
                            {
                                ((And)tar).InputA = null;
                                connections.Remove(c);
                                return;
                            }
                            else
                            {
                                ((And)tar).InputB = null;
                                connections.Remove(c);
                                return;
                            }
                        }
                        else if (tar.type == "Or")
                        {
                            if (((Or)tar).InputA == c)
                            {
                                ((Or)tar).InputA = null;
                                connections.Remove(c);
                                return;
                            }
                            else
                            {
                                ((Or)tar).InputB = null;
                                connections.Remove(c);
                                return;
                            }
                        }
                        else if (tar.type == "Not")
                        {
                            ((Not)tar).Input = null;
                            connections.Remove(c);
                            return;
                        }
                        else if (tar.type == "Sink")
                        {
                            ((Sink)tar).Input = null;
                            connections.Remove(c);
                            return;
                        }   
                    }
                }
            }
        }

        public bool moveElement(int x, int y, int newX, int newY)
        {
            int panelWidth = 991;
            int panelHeight = 583;
            int baseX = newX;
            int baseY = newY;
            Element el = getElement(x, y);
            if (el != null && ((newX > 0 && newY > 0 && newX <= panelWidth - 80 && newY <= panelHeight - 60)))
            {
                foreach (Connection c in connections)
                {
                    if (c.supplier == el)
                    {
                        if (newX + 80 > c.target.X)
                            return false;

                    }
                    else if (c.target == el)
                    {
                        if (newX < c.supplier.X + 80)
                            return false;
                    }
                }


                if (((isElement(newX, newY) && !(getElement(newX, newY) == el)) || (isElement(newX + 79, newY) && !(getElement(newX + 79, newY) == el)) ||
                       (isElement(newX + 79, newY + 59) && !(getElement(newX + 79, newY + 59) == el)) || (isElement(newX, newY + 59) && !(getElement(newX, newY + 59) == el)))) //While didn't reach borders;
                {
 
                    return false;
                }
                el.X = newX;
                el.Y = newY;
                return true;
            }
            return false;
        }

        public string getType(int x, int y)
        {
            if (getElement(x, y) != null)
                return getElement(x, y).type;
            else
                return "None";
        }
    }
}
